1 # INAV Programming Framework
3 INAV Programming Framework (IPF) is a mechanism that allows you to to create
4 custom functionality in INAV. You can choose for certain actions to be done,
5 based on custom conditions you select.
7 Logic conditions can be based on things such as RC channel values, switches, altitude,
8 distance, timers, etc. The conditions you create can also make use of other conditions
9 you've entered previously.
10 The results can be used in:
12 * [Servo mixer](Mixer.md) to activate/deactivate certain servo mix rulers
13 * To activate/deactivate system overrides
15 INAV Programming Framework consists of:
17 * Logic Conditions - each Logic Condition can be understood as a single command, a single line of code. Each logic condition consists of:
18 * an operator (action), such as "plus" or "set vtx power"
19 * one or two operands (nouns), which the action acts upon. Operands are often numbers, such as a channel value or the distance to home.
20 * "activator" condition - optional. This condition is only active when another condition is true
21 * Global Variables - variables that can store values from and for Logic Conditions and servo mixer
22 * Programming PID - general purpose, user configurable PID controllers
24 IPF can be edited using INAV Configurator user interface, or via CLI. To use COnfigurator, click the tab labeled
25 "Programming". The various options shown in Configurator are described below.
27 **Note:** IPF uses integer math. If your programming line returns a decimal, it will be truncated to an integer. So if your math is `1` / `3` = , IPF will truncate the decimal and return `0`.
33 `logic <rule> <enabled> <activatorId> <operation> <operand A type> <operand A value> <operand B type> <operand B value> <flags>`
35 * `<rule>` - ID of Logic Condition rule
36 * `<enabled>` - `0` evaluates as disabled, `1` evaluates as enabled
37 * `<activatorId>` - the ID of _LogicCondition_ used to activate this _Condition_. _Logic Condition_ will be evaluated only then Activator evaluates as `true`. `-1` evaluates as `true`
38 * `<operation>` - See `Operations` paragraph
39 * `<operand A type>` - See `Operands` paragraph
40 * `<operand A value>` - See `Operands` paragraph
41 * `<operand B type>` - See `Operands` paragraph
42 * `<operand B value>` - See `Operands` paragraph
43 * `<flags>` - See `Flags` paragraph
47 | Operation ID | Name | Notes |
48 |---------------|-------------------------------|-------|
49 | 0 | True | Always evaluates as true |
50 | 1 | Equal (A=B) | Evaluates `false` if `false` or `0` |
51 | 2 | Greater Than (A>B) | `true` if `Operand A` is a higher value than `Operand B` |
52 | 3 | Lower Than (A<B) | `true` if `Operand A` is a lower value than `Operand B` |
53 | 4 | Low | `true` if `<1333` |
54 | 5 | Mid | `true` if `>=1333 and <=1666` |
55 | 6 | High | `true` if `>1666` |
56 | 7 | AND | `true` if `Operand A` and `Operand B` are the same value or both `true` |
57 | 8 | OR | `true` if `Operand A` and/or `OperandB` is `true` |
58 | 9 | XOR | `true` if `Operand A` or `Operand B` is `true`, but not both |
59 | 10 | NAND | `false` if `Operand A` and `Operand B` are both `true`|
60 | 11 | NOR | `true` if `Operand A` and `Operand B` are both `false` |
61 | 12 | NOT | The boolean opposite to `Operand A` |
62 | 13 | Sticky | `Operand A` is the activation operator, `Operand B` is the deactivation operator. After the activation is `true`, the operator will return `true` until Operand B is evaluated as `true`|
63 | 14 | Basic: Add | Add `Operand A` to `Operand B` and returns the result |
64 | 15 | Basic: Subtract | Substract `Operand B` from `Operand A` and returns the result |
65 | 16 | Basic: Multiply | Multiply `Operand A` by `Operand B` and returns the result |
66 | 17 | Basic: Divide | Divide `Operand A` by `Operand B` and returns the result. NOTE: If `Operand B` = `0`, the `Divide` operation will simply return `Operand A`|
67 | 18 | Set GVAR | Store value from `Operand B` into the Global Variable addressed by `Operand A`. Bear in mind, that operand `Global Variable` means: Value stored in Global Variable of an index! To store in GVAR 1 use `Value 1` not `Global Variable 1` |
68 | 19 | Increase GVAR | Increase the GVAR indexed by `Operand A` (use `Value 1` for Global Variable 1) with value from `Operand B` |
69 | 20 | Decrease GVAR | Decrease the GVAR indexed by `Operand A` (use `Value 1` for Global Variable 1) with value from `Operand B` |
70 | 21 | Set IO Port | Set I2C IO Expander pin `Operand A` to value of `Operand B`. `Operand A` accepts values `0-7` and `Operand B` accepts `0` and `1` |
71 | 22 | Override Arming Safety | Allows the craft to arm on any angle even without GPS fix. WARNING: This bypasses all safety checks, even that the throttle is low, so use with caution. If you only want to check for certain conditions, such as arm without GPS fix. You will need to add logic conditions to check the throttle is low. |
72 | 23 | Override Throttle Scale | Override throttle scale to the value defined by operand. Operand type `0` and value `50` means throttle will be scaled by 50%. |
73 | 24 | Swap Roll & Yaw | basically, when activated, yaw stick will control roll and roll stick will control yaw. Required for tail-sitters VTOL during vertical-horizonral transition when body frame changes |
74 | 25 | Set VTx Power Level | Sets VTX power level. Accepted values are `0-3` for SmartAudio and `0-4` for Tramp protocol |
75 | 26 | Invert Roll | Inverts ROLL axis input for PID/PIFF controller |
76 | 27 | Invert Pitch | Inverts PITCH axis input for PID/PIFF controller |
77 | 28 | Invert Yaw | Inverts YAW axis input for PID/PIFF controller |
78 | 29 | Override Throttlw | Override throttle value that is fed to the motors by mixer. Operand is scaled in us. `1000` means throttle cut, `1500` means half throttle |
79 | 30 | Set VTx Band | Sets VTX band. Accepted values are `1-5` |
80 | 31 | Set VTx Channel | Sets VTX channel. Accepted values are `1-8` |
81 | 32 | Set OSD Layout | Sets OSD layout. Accepted values are `0-3` |
82 | 33 | Trigonometry: Sine | Computes SIN of `Operand A` value in degrees. Output is multiplied by `Operand B` value. If `Operand B` is `0`, result is multiplied by `500` |
83 | 34 | Trigonometry: Cosine | Computes COS of `Operand A` value in degrees. Output is multiplied by `Operand B` value. If `Operand B` is `0`, result is multiplied by `500` |
84 | 35 | Trigonometry: Tangent | Computes TAN of `Operand A` value in degrees. Output is multiplied by `Operand B` value. If `Operand B` is `0`, result is multiplied by `500` |
85 | 36 | Map Input | Scales `Operand A` from [`0` : `Operand B`] to [`0` : `1000`]. Note: input will be constrained and then scaled |
86 | 37 | Map Output | Scales `Operand A` from [`0` : `1000`] to [`0` : `Operand B`]. Note: input will be constrained and then scaled |
87 | 38 | Override RC Channel | Overrides channel set by `Operand A` to value of `Operand B`. Note operand A should normally be set as a "Value", NOT as "Get RC Channel"|
88 | 39 | Set Heading Target | Sets heading-hold target to `Operand A`, in centidegrees. Value wraps-around. |
89 | 40 | Modulo | Modulo. Divide `Operand A` by `Operand B` and returns the remainder |
90 | 41 | Override Loiter Radius | Sets the loiter radius to `Operand A` [`0` : `100000`] in cm. Must be larger than the loiter radius set in the **Advanced Tuning**. |
91 | 42 | Set Control Profile | Sets the active config profile (PIDFF/Rates/Filters/etc) to `Operand A`. `Operand A` must be a valid profile number, currently from 1 to 3. If not, the profile will not change |
92 | 43 | Use Lowest Value | Finds the lowest value of `Operand A` and `Operand B` |
93 | 44 | Use Highest Value | Finds the highest value of `Operand A` and `Operand B` |
94 | 45 | Flight Axis Angle Override | Sets the target attitude angle for axis. In other words, when active, it enforces Angle mode (Heading Hold for Yaw) on this axis (Angle mode does not have to be active). `Operand A` defines the axis: `0` - Roll, `1` - Pitch, `2` - Yaw. `Operand B` defines the angle in degrees |
95 | 46 | Flight Axis Rate Override | Sets the target rate (rotation speed) for axis. `Operand A` defines the axis: `0` - Roll, `1` - Pitch, `2` - Yaw. `Operand B` defines the rate in degrees per second |
96 | 47 | Edge | Momentarily true when triggered by `Operand A`. `Operand A` is the activation operator [`boolean`], `Operand B` _(Optional)_ is the time for the edge to stay active [ms]. After activation, operator will return `true` until the time in Operand B is reached. If a pure momentary edge is wanted. Just leave `Operand B` as the default `Value: 0` setting. |
97 | 48 | Delay | Delays activation after being triggered. This will return `true` when `Operand A` _is_ true, and the delay time in `Operand B` [ms] has been exceeded. |
98 | 49 | Timer | A simple on - off timer. `true` for the duration of `Operand A` [ms]. Then `false` for the duration of `Operand B` [ms]. |
99 | 50 | Delta (|A| >= B) | This returns `true` when the value of `Operand A` has changed by the value of `Operand B` or greater within 100ms. |
100 | 51 | Approx Equals (A ~ B) | `true` if `Operand B` is within 1% of `Operand A`. |
101 | 52 | LED Pin PWM | Value `Operand A` from [`0` : `100`] starts PWM generation on LED Pin. See [LED pin PWM](LED%20pin%20PWM.md). Any other value stops PWM generation (stop to allow ws2812 LEDs updates in shared modes). |
102 | 53 | Disable GPS Sensor Fix | Disables the GNSS sensor fix. For testing GNSS failure. |
103 | 54 | Mag calibration | Trigger a magnetometer calibration. |
107 | Operand Type | Name | Notes |
108 |---------------|-----------------------|-------|
109 | 0 | Value | Value derived from `value` field |
110 | 1 | Get RC Channel | `value` points to RC channel number, indexed from 1 |
111 | 2 | Flight | `value` points to **Flight** Parameters table |
112 | 3 | Flight Mode | `value` points to **Flight_Mode** table |
113 | 4 | Logic Condition | `value` points to other logic condition ID |
114 | 5 | Get Global Variable | Value stored in Global Variable indexed by `value`. `GVAR 1` means: value in GVAR 1 |
115 | 5 | Programming PID | Output of a Programming PID indexed by `value`. `PID 1` means: value in PID 1 |
116 | 6 | Waypoints | `value` points to the **Waypoint** parameter table |
118 #### Flight Parameters
120 | Operand Value | Name | Notes |
121 |---------------|---------------------------------------|-------|
122 | 0 | ARM Timer [s] | Time since armed in `seconds` |
123 | 1 | Home Distance [m] | distance from home in `meters` |
124 | 2 | Trip distance [m] | Trip distance in `meters` |
126 | 4 | Vbat [centi-Volt] [1V = 100] | VBAT Voltage in `Volts * 100`, eg. `12.1V` is `1210` |
127 | 5 | Cell voltage [centi-Volt] [1V = 100] | Average cell voltage in `Volts * 100`, eg. `12.1V` is `1210` |
128 | 6 | Current [centi-Amp] [1A = 100] | Current in `Amps * 100`, eg. `9A` is `900` |
129 | 7 | Current drawn [mAh] | Total used current in `mAh` |
131 | 9 | Ground speed [cm/s] | Ground speed in `cm/s` |
132 | 10 | 3D speed [cm/s] | 3D speed in `cm/s` |
133 | 11 | Air speed [cm/s] | Air speed in `cm/s` |
134 | 12 | Altitude [cm] | Altitude in `cm` |
135 | 13 | Vertical speed [cm/s] | Vertical speed in `cm/s` |
136 | 14 | Throttle position [%] | Throttle position in `%` |
137 | 15 | Roll [deg] | Roll attitude in `degrees` |
138 | 16 | Pitch [deg] | Pitch attitude in `degrees` |
139 | 17 | Is Armed | Is the system armed? boolean `0`/`1` |
140 | 18 | Is Autolaunch | Is auto launch active? boolean `0`/`1` |
141 | 19 | Is Controlling Altitude | Is altitude being controlled? boolean `0`/`1` |
142 | 20 | Is Controlling Position | Is the position being controlled? boolean `0`/`1` |
143 | 21 | Is Emergency Landing | Is the aircraft emergency landing? boolean `0`/`1` |
144 | 22 | Is RTH | Is RTH active? boolean `0`/`1` |
145 | 23 | Is Landing | Is the aircaft automatically landing? boolean `0`/`1` |
146 | 24 | Is Failsafe | Is the flight controller in a failsafe? boolean `0`/`1` |
147 | 25 | Stabilized Roll | Roll PID controller output `[-500:500]` |
148 | 26 | Stabilized Pitch | Pitch PID controller output `[-500:500]` |
149 | 27 | Stabilized Yaw | Yaw PID controller output `[-500:500]` |
150 | 28 | 3D home distance [m] | 3D distance to home in `meters`. Calculated from Home distance and Altitude using Pythagorean theorem |
151 | 29 | CRSF LQ | Link quality as returned by the CRSF protocol |
152 | 30 | CRSF SNR | SNR as returned by the CRSF protocol |
153 | 31 | GPS Valid Fix | Boolean `0`/`1`. True when the GPS has a valid 3D Fix |
154 | 32 | Loiter Radius [cm] | The current loiter radius in cm. |
155 | 33 | Active Control Profile | Integer for the active config profile `[1..MAX_PROFILE_COUNT]` |
156 | 34 | Battery cells | Number of battery cells detected |
157 | 35 | AGL status [0/1] | Boolean `1` when AGL can be trusted, `0` when AGL estimate can not be trusted |
158 | 36 | AGL [cm] | Integer altitude above The Groud Altitude in `cm` |
159 | 37 | Rangefinder [cm] | Integer raw distance provided by the rangefinder in `cm` |
160 | 38 | Active Mixer Profile | Which mixer is currently active (for vtol etc) |
161 | 39 | Mixer Transition Active | Boolean `0`/`1`. Are we currently switching between mixers (quad to plane etc) |
162 | 40 | Yaw [deg] | Current heading (yaw) in `degrees` |
163 | 41 | FW Land Sate | Integer `1` - `5`, indicates the status of the FW landing, 0 Idle, 1 Downwind, 2 Base Leg, 3 Final Approach, 4 Glide, 5 Flare |
164 | 42 | Current battery profile | The active battery profile. Integer `[1..MAX_PROFILE_COUNT]` |
165 | 43 | Flown Loiter Radius [m] | The actual loiter radius flown by a fixed wing during hold modes, in `meters` |
169 The flight mode operands return `true` when the mode is active. These are modes that you will see in the **Modes** tab. Note: the `USER*` modes are used by camera switchers, PINIO etc. They are not the Waypoint User Actions. See the [Waypoints](#waypoints) section to access those.
171 | Operand Value | Name | Notes |
172 |---------------|-------------------|-------|
173 | 0 | Failsafe | `true` when a **Failsafe** state has been triggered. |
174 | 1 | Manual | `true` when you are in the **Manual** flight mode. |
175 | 2 | RTH | `true` when you are in the **Return to Home** flight mode. |
176 | 3 | Position Hold | `true` when you are in the **Position Hold** or **Loiter** flight modes. |
177 | 4 | Cruise | `true` when you are in the **Cruise** flight mode. |
178 | 5 | Altitude Hold | `true` when you the **Altitude Hold** flight mode modifier is active. |
179 | 6 | Angle | `true` when you are in the **Angle** flight mode. |
180 | 7 | Horizon | `true` when you are in the **Horizon** flight mode. |
181 | 8 | Air | `true` when you the **Airmode** flight mode modifier is active. |
182 | 9 | USER 1 | `true` when the **USER 1** mode is active. |
183 | 10 | USER 2 | `true` when the **USER 2** mode is active. |
184 | 11 | Course Hold | `true` when you are in the **Course Hold** flight mode. |
185 | 12 | USER 3 | `true` when the **USER 3** mode is active. |
186 | 13 | USER 4 | `true` when the **USER 4** mode is active. |
187 | 14 | Acro | `true` when you are in the **Acro** flight mode. |
188 | 15 | Waypoint Mission | `true` when you are in the **WP Mission** flight mode. |
192 | Operand Value | Name | Notes |
193 |---------------|-------------------------------|-------|
194 | 0 | Is WP | Boolean `0`/`1` |
195 | 1 | Current Waypoint Index | Current waypoint leg. Indexed from `1`. To verify WP is in progress, use `Is WP` |
196 | 2 | Current Waypoint Action | `true` when Action active in current leg. See ACTIVE_WAYPOINT_ACTION table |
197 | 3 | Next Waypoint Action | `true` when Action active in next leg. See ACTIVE_WAYPOINT_ACTION table |
198 | 4 | Distance to next Waypoint | Distance to next WP in metres |
199 | 5 | Distance from Waypoint | Distance from the last WP in metres |
200 | 6 | User Action 1 | `true` when User Action 1 is active on this waypoint leg [boolean `0`/`1`] |
201 | 7 | User Action 2 | `true` when User Action 2 is active on this waypoint leg [boolean `0`/`1`] |
202 | 8 | User Action 3 | `true` when User Action 3 is active on this waypoint leg [boolean `0`/`1`] |
203 | 9 | User Action 4 | `true` when User Action 4 is active on this waypoint leg [boolean `0`/`1`] |
204 | 10 | Next Waypoint User Action 1 | `true` when User Action 1 is active on the next waypoint leg [boolean `0`/`1`] |
205 | 11 | Next Waypoint User Action 2 | `true` when User Action 2 is active on the next waypoint leg [boolean `0`/`1`] |
206 | 12 | Next Waypoint User Action 3 | `true` when User Action 3 is active on the next waypoint leg [boolean `0`/`1`] |
207 | 13 | Next Waypoint User Action 4 | `true` when User Action 4 is active on the next waypoint leg [boolean `0`/`1`] |
210 #### ACTIVE_WAYPOINT_ACTION
213 |---------------|-------|
224 All flags are reseted on ARM and DISARM event.
226 | bit | Decimal | Function |
227 |-------|-----------|-----------|
228 | 0 | 1 | Latch - after activation LC will stay active until LATCH flag is reset |
229 | 1 | 2 | Timeout satisfied - Used in timed operands to determine if the timeout has been met |
235 `gvar <index> <default value> <min> <max>`
237 **Note:** Global Variables (GVARs) are limited to integers between negative `-32768` and positive `32767`.
241 IPF makes a set of general user PIDFF controllers avaliable for use in your program. These PIDFF controllers are not tied to any roll/pitch/yaw profiles or other controls.
242 The output of these controllers can be used in an IPF program by using the `Programming PID` operand.
243 The `<setpoint value>` of the controller is the target value for the controller to hit. The `<measurement value>` is the measurement of the current value. For instance, `<setpoint value>` could be the speed you want to go, and `<measurement value>` is the current speed.
244 P, I, D, and FF values will need to be manually adjusted to determine the appropriate value for the program and controller.
246 `pid <index> <enabled> <setpoint type> <setpoint value> <measurement type> <measurement value> <P gain> <I gain> <D gain> <FF gain>`
248 * `<index>` - ID of PID Controller, starting from `0`
249 * `<enabled>` - `0` evaluates as disabled, `1` evaluates as enabled
250 * `<setpoint type>` - See `Operands` paragraph
251 * `<setpoint value>` - See `Operands` paragraph
252 * `<measurement type>` - See `Operands` paragraph
253 * `<measurement value>` - See `Operands` paragraph
254 * `<P gain>` - P-gain, scaled to `1/1000`
255 * `<I gain>` - I-gain, scaled to `1/1000`
256 * `<D gain>` - D-gain, scaled to `1/1000`
257 * `<FF gain>` - FF-gain, scaled to `1/1000`
261 ### When more than 100 meters away, increase VTX power
262 ![screenshot of vtx home distance](./assets/images/vtx_home_distance.png)
264 ### When more than 600 meters away, engage return-to-home by setting the matching RC channel
265 ![screenshot of rth home distance](./assets/images/rth_home_distance.jpg)
268 ### Dynamic THROTTLE scale
270 `logic 0 1 0 23 0 50 0 0 0`
272 Limits the THROTTLE output to 50% when Logic Condition `0` evaluates as `true`
274 ### Set VTX power level via Smart Audio
276 `logic 0 1 0 25 0 3 0 0 0`
278 Sets VTX power level to `3` when Logic Condition `0` evaluates as `true`
280 ### Invert ROLL and PITCH when rear facing camera FPV is used
282 Solves the problem from [https://github.com/iNavFlight/inav/issues/4439](https://github.com/iNavFlight/inav/issues/4439)
285 logic 0 1 0 26 0 0 0 0 0
286 logic 1 1 0 27 0 0 0 0 0
289 Inverts ROLL and PITCH input when Logic Condition `0` evaluates as `true`. Moving Pitch stick up will cause pitch down (up for rear facing camera). Moving Roll stick right will cause roll left of a quad (right in rear facing camera)
291 ### Cut motors but keep other throttle bindings active
293 `logic 0 1 0 29 0 1000 0 0 0`
295 Sets throttle output to `0%` when Logic Condition `0` evaluates as `true`
297 ### Set throttle to 50% and keep other throttle bindings active
299 `logic 0 1 0 29 0 1500 0 0 0`
301 Sets throttle output to about `50%` when Logic Condition `0` evaluates as `true`
303 ### Set throttle control to different RC channel
305 `logic 0 1 0 29 1 7 0 0 0`
307 If Logic Condition `0` evaluates as `true`, motor throttle control is bound to RC channel 7 instead of throttle channel
309 ### Set VTX channel with a POT
311 Set VTX channel with a POT on the radio assigned to RC channel 6
314 logic 0 1 -1 15 1 6 0 1000 0
315 logic 1 1 -1 37 4 0 0 7 0
316 logic 2 1 -1 14 4 1 0 1 0
317 logic 3 1 -1 31 4 2 0 0 0
321 1. Normalize range `[1000:2000]` to `[0:1000]` by substracting `1000`
322 2. Scale range `[0:1000]` to `[0:7]`
323 3. Increase range by `1` to have the range of `[1:8]`
324 4. Assign LC#2 to VTX channel function
326 ### Set VTX power with a POT
328 Set VTX power with a POT on the radio assigned to RC channel 6. In this example we scale POT to 4 power level `[1:4]`
331 logic 0 1 -1 15 1 6 0 1000 0
332 logic 1 1 -1 37 4 0 0 3 0
333 logic 2 1 -1 14 4 1 0 1 0
334 logic 3 1 -1 25 4 2 0 0 0
338 1. Normalize range [1000:2000] to [0:1000] by substracting `1000`
339 2. Scale range [0:1000] to [0:3]
340 3. Increase range by `1` to have the range of [1:4]
341 4. Assign LC#2 to VTX power function
343 ## Common issues / questions about IPF
345 One common mistake involves setting RC channel values. To override (set) the
346 value of a specific RC channel, choose "Override RC value", then for operand A
347 choose *value* and enter the channel number. Choosing "get RC value" is a common mistake,
348 which does something other than what you probably want.
350 ![screenshot of override an RC channel with a value](./assets/images/ipf_set_get_rc_channel.png)